import { comps, groups, plugins, demos, groupData,pluginGroups,pluginGroupData,pluginDemos } from "./data";
import handlebars from "handlebars";
import { readFileSync, writeFile,existsSync,copyFileSync,readdirSync,statSync,mkdirSync, } from "fs";
// @ts-ignore
import { outputFileSync,moveSync } from "fs-extra/esm"
import { resolve, dirname,join } from "path";
import { fileURLToPath } from "url";
import {kebabCase,camelCase,upperFirst} from "lodash-es"
import { treeReset } from "./tree"
//
import cfg from "./config"
//
handlebars.registerHelper('stringify', function(obj) {
  return new handlebars.SafeString(JSON.stringify(obj,null,2));
});
//
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const currentLocal = cfg.useI18n ? '/' + cfg.local : ''
//
const updateFile = (tplPath:string,filePath:string,ctx:Record<string,any> = {}) => {
  const tpl = readFileSync(tplPath,"utf-8")
  const content = handlebars.compile(tpl,{ noEscape: true})({...ctx,cfg,currentLocal});
  outputFileSync(filePath,content)
}
//
const saveData = () => {
  const fPath = resolve(__dirname, "./data.ts");
  const tPath = resolve(__dirname, "../template/data.ts.tpl");
  const tpl = readFileSync(tPath, "utf-8");
  //
  const c = handlebars.compile(tpl, { noEscape: true })({
    comps:JSON.stringify(comps,null,2),
    groups:JSON.stringify(groups,null,2),
    plugins:JSON.stringify(plugins,null,2),
    demos:JSON.stringify(demos,null,2),
    groupData:JSON.stringify(groupData,null,2),
    pluginGroups:JSON.stringify(pluginGroups,null,2),
    pluginGroupData:JSON.stringify(pluginGroupData,null,2),
    pluginDemos:JSON.stringify(pluginDemos,null,2),
  });
  writeFile(fPath, c, (err) => {
    // console.log('===>',err)
    // console.error(err);
    // console.warn(fPath);
    // console.warn(c);
    // console.warn("=======END========");
  });
};
//
const clearData = () => {
  //
  const fc = (obj:any) => {
    const ks = Object.keys(obj)
    ks.forEach(k => {
      delete obj[k]
    })
  }
  //
  fc(comps)
  fc(demos)
  fc(groups)
  fc(plugins)
  fc(groupData)
  fc(pluginGroups)
  fc(pluginGroupData)
  fc(pluginDemos)
  //
  saveData();
  //
  docsRefresh();
}
const sortData = (keys:string[],obj:Record<string,any>) => {
  return keys.sort((k1,k2) => obj[k1].sort - obj[k2].sort)
}
const copyFolderSync = (folderPath:string, destFolder:string) => {
  if(!existsSync(folderPath)){
    return ;
  }
  const files = readdirSync(folderPath);
  if(!existsSync(destFolder)){
    mkdirSync(destFolder, { recursive: true });
  }
  // 逐个处理文件
  files.forEach(file => {
    const filePath = join(folderPath, file);
    const stats = statSync(filePath);
    if (stats.isDirectory()) {
      // 如果是文件夹，则递归处理子文件夹
      const childFolder = join(destFolder, file);
      mkdirSync(childFolder, { recursive: true });
      copyFolderSync(filePath, childFolder);
    } else {
      // 如果是文件，则判断目标文件夹中是否已经存在同名文件，如果不存在则复制
      const destPath = join(destFolder, file);
      if (!existsSync(destPath)) {
        copyFileSync(filePath, destPath);
      }
    }
  });
}

//
const createGroupTree = (groupObj:any,itemObj:any,groupItems:any) => {
  const ks = Object.keys(groupObj).filter(k => groupObj[k].parent == 'root')
  const rks = sortData(ks,groupObj)
  const f = (list:string[]) => {
    let res:any = [] = []
    let cps:string[] = []
    let gps:string[] = []
    // 分类
    list.forEach(k => {
      if(groupItems[k] && groupItems[k].length > 0){
        gps.push(k)
      }
      if(itemObj[k]){
        cps.push(k)
      }
    })
    // 排序
    cps = sortData(cps,itemObj);
    gps = sortData(gps,groupObj);
    res = new Array().concat(gps.map(k => ({...groupObj[k],children:f(groupItems[k])})),cps.map(k => itemObj[k]))
    return res;
  }
  const tree = f(rks);
  return tree;
}
const addData = () => {}
// docs
const docsRefresh = () => {
  docsRefreshSlide();
  docsRefreshIndex();
}
// 刷新文档菜单
const docsRefreshSlide = () => {
  // 菜单，导航
  const componentsContent = treeReset(createGroupTree(groupData,comps,groups),(n) => {
    const res:any = {}
    res.text = n.menu || n.label;
    if(n.children == undefined){
      res.link = `/${cfg.docs.component}${currentLocal}/${n.group}/${n.name}.md`
    }else{
      res.collapsed = cfg.docs.slideCollapsed;
    }
    return res;
  },{sourceChildren:"children",children:"items"})

  const pluginsContent = treeReset(createGroupTree(pluginGroupData,plugins,pluginGroups),(n) => {
    const res:any = {}
    res.text = n.menu || n.label;
    if(n.children == undefined){
      res.link = `/${cfg.docs.plugin}${cfg.useI18n ? '/' + cfg.local : ''}/${n.group}/${n.name}.md`
    }
    else{
      res.collapsed = cfg.docs.slideCollapsed;
    }
    return res;
  },{sourceChildren:"children",children:"items"})

  const tplPath = resolve(__dirname,"../template/slide.config.tpl");
  const filePath = resolve(__dirname,"../../docs/.vitepress/config/slide.config.ts");
  //
  updateFile(tplPath,filePath,{components:JSON.stringify(componentsContent,null,2),plugins:JSON.stringify(pluginsContent,null,2)})
}
// 刷新文档配置项
const docsRefreshIndex = () => {
  const indexTP = resolve(__dirname,"../template/docs.index.tpl");
  const navTP = resolve(__dirname,"../template/docs.nav.tpl");
  const configTP = resolve(__dirname,"../template/docs.config.tpl");
  const indexFP = resolve(__dirname,"../../docs/index.md");
  const navFP = resolve(__dirname,"../../docs/.vitepress/config/nav.config.ts");
  const configFP = resolve(__dirname,"../../docs/.vitepress/config/docs.config.ts");

  updateFile(indexTP,indexFP)
  updateFile(navTP,navFP)
  updateFile(configTP,configFP)
}
const docsClear = () => {
  clearData();
  docsRefresh();
}
// 生成国际化结构并复制当前语言的文件
const docsResetI18n = () => {
  const local = currentLocal;
  const langs = cfg.langs;
  const sdp = resolve(__dirname,`../../docs/${cfg.docs.demo}/${local}`)
  const slp = resolve(__dirname,`../../docs/${cfg.docs.component}/${local}`)
  const spp = resolve(__dirname,`../../docs/${cfg.docs.plugin}/${local}`)
  for(let n of langs){
    if(n != local){
      const tdp = resolve(__dirname,`../../docs/${cfg.docs.demo}/${n}`)
      const tlp = resolve(__dirname,`../../docs/${cfg.docs.component}/${n}`)
      const tpp = resolve(__dirname,`../../docs/${cfg.docs.plugin}/${n}`)
      copyFolderSync(sdp,tdp);
      copyFolderSync(slp,tlp);
      copyFolderSync(spp,tpp);
    }
  }
}
/* comps */
const compAdd = (d:any,isAddDemo?:boolean) => {
  const {name:n,group} = d;
  const name = upperFirst(camelCase(n));
  // 生成 came-case name
  const kebabName = kebabCase(name)
  //
  groups[group].push(name)
  comps[name] = {...d,name,kebabName,demos:[]};
  saveData();
  // 如果没有新增 DEMO 生成组件文档
  !isAddDemo && compRefreshMd(comps[name]);
  // 生成组件文件
  compRefreshMain(comps[name])
  // 刷新组件库入口文件
  compsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const compDel = (n:string | any) => {
  const d = typeof n === 'string' ? comps[n] : n;
  if(!d) {throw new Error("没有找到匹配的组件数据！")}
  const {name,group} = d;
  delete comps[name];
  groups[group] = groups[group].filter(n => n != name);
  //
  saveData();
  // 刷新组件库入口文件
  compsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const compAddGroup = (d:any) => {
  const {name,parent} = d;
  groupData[name] = d;
  groups[name] = [];
  if(parent != "root" && groups[parent] != undefined){
    groups[parent].push(name)
  }
  //
  saveData();
}
const compDelGroup = (n:string | any) => {
  const d = typeof n === 'string' ? groupData[n] : n;
  const {name,parent} = d;
  if(groups[name].length > 0){
    throw new Error("分组已被使用，请解除引用之后再删除")
  }
  delete groupData[name];
  delete groups[name];
  if(parent != "root" && groups[parent] != undefined){
    groups[parent] = groups[parent].filter(n => n!= name)
  }
  //
  saveData();
  //
}
const compChangeGroup = (compName:string,groupName:string) => {
  const d = comps[compName];
  const {name,group,demos:ds} = d;
  //
  comps[name] = {...d,group:groupName};
  groups[group] = groups[group].filter(n => n != name)
  groups[groupName].push(name)
  //
  saveData();
  // 移动当前路径文件到新的路径
  const sps = [
    resolve(__dirname,`../../docs/${cfg.docs.component}${currentLocal}/${group}/${name}.md`),
    resolve(__dirname,`../../${cfg.component.path}/${group}/${name}`),
    ...ds.map((n:string) => resolve(__dirname,`../../docs/${cfg.docs.demo}${currentLocal}/${group}/${n}.vue`))
  ]
  const tps = [
    resolve(__dirname,`../../docs/${cfg.docs.component}${currentLocal}/${groupName}/${name}.md`),
    resolve(__dirname,`../../${cfg.component.path}/${groupName}/${name}`),
    ...ds.map((n:string) => resolve(__dirname,`../../docs/${cfg.docs.demo}${currentLocal}/${groupName}/${n}.vue`))
  ]
  for(let i = 0,l = sps.length; i < l;i++){
    const sp = sps[i]
    const tp = tps[i]
    moveSync(sp,tp)
  }
  // 刷新组件库入口文件
  compsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const compAddDemo = (d:any) => {
  const {name:n,upper} = d
  const name = `${upper}${upperFirst(n)}`;
  demos[name] = {...d,name};
  comps[upper].demos = sortData([...comps[upper].demos,name],demos) // 排序
  //
  saveData();
  // 生成 DEMO 文件
  compSetDemo(demos[name]);
  // 刷新 组件文档
  compRefreshMd(comps[upper])
}
const compDelDemo = (n:string | any) => {
  const d = typeof n === 'string' ? demos[n] : n;
  const {name,upper} = d
  delete demos[name];
  comps[upper].demos = comps[upper].demos.filter((n:string) => n != name)
  //
  saveData();
  // 刷新 组件文档
  compRefreshMd(comps[upper])
}
const compSetDemo = (d:any) => {
  const {name,group,upper} = d
  const filePath = resolve(__dirname,`../../docs/${cfg.docs.demo}/${group}/${upper}/${name}.vue`);
  // if(!existsSync(filePath)){
    const tplPath = resolve(__dirname,"../template/demo.vue.tpl")
    updateFile(tplPath,filePath,d)
  // }
}
const compRefresh = (d:any) => {
  compRefreshDocs(d);
  compRefreshMain(d);
  //
  docsRefreshSlide();
  compsRefreshIndex();
}
const compRefreshDocs = (d:any) => {
  compRefreshDemos(d)
  compRefreshMd(d);
}
const compRefreshMd = (d:any) => {
  const {group,name,demos:ds} = d;
  const filePath = resolve(__dirname,`../../docs/${cfg.docs.component}${currentLocal}/${group}/${name}.md`)
  const tplPath = resolve(__dirname,"../template/comp.md.tpl")
  //
  const demosData = ds.map((n:string) => demos[n])
  //
  const tpl = readFileSync(tplPath,"utf-8");
  const tplContent = handlebars.compile(tpl,{ noEscape: true})({...d,demos:demosData,cfg,currentLocal});
  // 非覆盖式更新
  if(!existsSync(filePath)){
    outputFileSync(filePath,tplContent)
  }else{
    const REG = /\<script[\s\S]+\<\/script\>/i;
    const cs = readFileSync(filePath,"utf-8");
    const tsc = REG.exec(tplContent);
    //
    let fs = cs;
    if(tsc){
      fs = fs.replace(REG,tsc[0])
    }
    const DREG = /\[DEMO START\][\s\S]+\[DEMO END\]/i;
    const dts = DREG.exec(tplContent)
    if(dts){
      fs = fs.replace(DREG,dts[0])
    }
    outputFileSync(filePath,fs)
  }
}
const compRefreshDemos = (d:any) => {
  const {demos:ds} = d;
  for(let n of ds){
    const demo = demos[n];
    compSetDemo(demo)
  }
}
const compRefreshMain = (d:any) => {
  const {name,group} = d;
  const indexTplPath = resolve(__dirname,"../template/comp.ts.tpl")
  const indexFilePath = resolve(__dirname,`../../${cfg.component.path}/${group}/${name}/${cfg.component.entry}`);
  const vueTplPath = resolve(__dirname,"../template/comp.vue.tpl")
  const vueFilePath = resolve(__dirname,`../../${cfg.component.path}/${group}/${name}/${cfg.component.vue}`)
  const interTplPath = resolve(__dirname,"../template/comp.inter.tpl")
  const interFilePath = resolve(__dirname,`../../${cfg.component.path}/${group}/${name}/${cfg.component.inter}`);
  //
  updateFile(vueTplPath,vueFilePath,d)
  updateFile(interTplPath,interFilePath,d)
  updateFile(indexTplPath,indexFilePath,d)
}
// 刷新组件库入口文件
const compsRefreshIndex = () => {
  const cs = Object.keys(comps).map(k => comps[k]);
  //
  const tplPath = resolve(__dirname,"../template/libs.index.tpl");
  const filePath = resolve(__dirname,`../../${cfg.component.path}/index.ts`);
  updateFile(tplPath,filePath,{comps:cs})
}
// 重建组件库所有文件
const compsRebuild = () => {}

/* plugins */
const pluginAdd = (d:any,isAddDemo?:boolean) => {
  const {name:n,group} = d;
  const name = upperFirst(camelCase(n));
  // 生成 came-case name
  const kebabName = kebabCase(name)
  //
  pluginGroups[group].push(name)
  plugins[name] = {...d,name,kebabName,demos:[]};
  saveData();
  // 如果没有新增 DEMO 生成组件文档
  !isAddDemo && pluginRefreshMd(plugins[name]);
  // 生成组件文件
  pluginRefreshMain(plugins[name])
  // 刷新组件库入口文件
  pluginsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const pluginDel = (n:string | any) => {
  const d = typeof n === 'string' ? plugins[n] : n;
  if(!d) {throw new Error("没有找到匹配的组件数据！")}
  const {name,group} = d;
  delete plugins[name];
  pluginGroups[group] = pluginGroups[group].filter(n => n != name);
  //
  saveData();
  // 刷新组件库入口文件
  pluginsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const pluginAddGroup = (d:any) => {
  const {name,parent} = d;
  pluginGroupData[name] = d;
  pluginGroups[name] = [];
  if(parent != "root" && pluginGroups[parent] != undefined){
    pluginGroups[parent].push(name)
  }
  //
  saveData();
}
const pluginDelGroup = (n:string | any) => {
  const d = typeof n === 'string' ? pluginGroupData[n] : n;
  const {name,parent} = d;
  if(pluginGroups[name].length > 0){
    throw new Error("分组已被使用，请解除引用之后再删除")
  }
  delete pluginGroupData[name];
  delete pluginGroups[name];
  if(parent != "root" && pluginGroups[parent] != undefined){
    pluginGroups[parent] = pluginGroups[parent].filter(n => n!= name)
  }
  //
  saveData();
}
const pluginChangeGroup = (pluginName:string,groupName:string) => {
  const d = plugins[pluginName];
  const {name,group,demos:ds} = d;
  //
  plugins[name] = {...d,group:groupName};
  pluginGroups[group] = pluginGroups[group].filter(n => n != name)
  pluginGroups[groupName].push(name)
  //
  saveData();
  // 移动当前路径文件到新的路径
  const sps = [
    resolve(__dirname,`../../docs/${cfg.docs.plugin}${currentLocal}/${group}/${name}.md`),
    resolve(__dirname,`../../${cfg.plugin.path}/${group}/${name}`),
    ...ds.map((n:string) => resolve(__dirname,`../../docs/${cfg.docs.pluginDemo}${currentLocal}/${group}/${n}.vue`))
  ]
  const tps = [
    resolve(__dirname,`../../docs/${cfg.docs.component}${currentLocal}/${groupName}/${name}.md`),
    resolve(__dirname,`../../${cfg.component.path}/${groupName}/${name}`),
    ...ds.map((n:string) => resolve(__dirname,`../../docs/${cfg.docs.pluginDemo}${currentLocal}/${groupName}/${n}.vue`))
  ]
  for(let i = 0,l = sps.length; i < l;i++){
    const sp = sps[i]
    const tp = tps[i]
    moveSync(sp,tp)
  }
  // 刷新组件库入口文件
  pluginsRefreshIndex();
  // 刷新文档菜单
  docsRefreshSlide();
}
const pluginAddDemo = (d:any) => {
  const {name:n,upper} = d
  const name = `${upper}${upperFirst(n)}`;
  pluginDemos[name] = {...d,name};
  plugins[upper].demos = sortData([...plugins[upper].demos,name],pluginDemos) // 排序
  //
  saveData();
  // 生成 DEMO 文件
  pluginSetDemo(pluginDemos[name]);
  // 刷新 组件文档
  pluginRefreshMd(plugins[upper])
}
const pluginDelDemo = (n:string | any) => {
  const d = typeof n === 'string' ? pluginDemos[n] : n;
  const {name,upper} = d
  delete pluginDemos[name];
  plugins[upper].demos = plugins[upper].demos.filter((n:string) => n != name)
  //
  saveData();
  // 刷新 组件文档
  pluginRefreshMd(plugins[upper])
}
const pluginSetDemo = (d:any) => {
  const {name,group,upper} = d
  const filePath = resolve(__dirname,`../../docs/${cfg.docs.pluginDemo}/${group}/${upper}/${name}.vue`);
  // if(!existsSync(filePath)){
    const tplPath = resolve(__dirname,"../template/plugin.demo.tpl")
    updateFile(tplPath,filePath,d)
  // }
}
const pluginRefresh = (d:any) => {
  pluginRefreshDocs(d);
  pluginRefreshMain(d);
  //
  docsRefreshSlide();
  pluginsRefreshIndex();
}
const pluginRefreshDocs = (d:any) => {
  pluginRefreshDemos(d)
  pluginRefreshMd(d)
}
const pluginRefreshMd = (d:any) => {
  const {name,group,demos:ds = []} = d;
  const filePath = resolve(__dirname,`../../docs/${cfg.docs.plugin}${currentLocal}/${group}/${name}.md`)
  const tplPath = resolve(__dirname,"../template/plugin.md.tpl")
  //
  const demosData = ds.map((n:string) => pluginDemos[n])
  //
  const tpl = readFileSync(tplPath,"utf-8");
  const tplContent = handlebars.compile(tpl,{ noEscape: true})({...d,demos:demosData,cfg,currentLocal});
  // 非覆盖式更新
  if(!existsSync(filePath)){
    outputFileSync(filePath,tplContent)
  }else{
    const REG = /\<script[\s\S]+\<\/script\>/i;
    const cs = readFileSync(filePath,"utf-8");
    const tsc = REG.exec(tplContent);
    //
    let fs = cs;
    if(tsc){
      fs = fs.replace(REG,tsc[0])
    }
    const DREG = /\[DEMO START\][\s\S]+\[DEMO END\]/i;
    const dts = DREG.exec(tplContent)
    if(dts){
      fs = fs.replace(DREG,dts[0])
    }
    outputFileSync(filePath,fs)
  }
}
const pluginRefreshDemos = (d:any) => {
  const {demos:ds} = d;
  for(let n of ds){
    const demo = pluginDemos[n];
    pluginSetDemo(demo);
  }
}
const pluginRefreshMain = (d:any) => {
  const {name,group} = d;
  const indexTplPath = resolve(__dirname,"../template/plugin.ts.tpl")
  const indexFilePath = resolve(__dirname,`../../${cfg.plugin.path}/${group}/${name}/${cfg.plugin.entry}`)
  const interTplPath = resolve(__dirname,"../template/plugin.inter.tpl")
  const interFilePath = resolve(__dirname,`../../${cfg.plugin.path}/${group}/${name}/${cfg.plugin.inter}`);
  //
  updateFile(indexTplPath,indexFilePath,d)
  updateFile(interTplPath,interFilePath,d)
}
const pluginsRefreshIndex = () => {
  const ps = sortData(Object.keys(plugins),plugins).map(k => plugins[k]);
  const filePath = resolve(__dirname,`../../${cfg.plugin.path}/index.ts`);
  const tplPath = resolve(__dirname,"../template/plugin.index.tpl")
  updateFile(tplPath,filePath,{cfg,currentLocal,plugins:ps})
}

export const docUtils = {
  refresh:docsRefresh,
  refreshSlide:docsRefreshSlide,
  refreshIndex:docsRefreshIndex,
  clear:docsClear,
  resetI18n:docsResetI18n
}
export const compUtils = {
  add:compAdd,
  del:compDel,
  addGroup:compAddGroup,
  delGroup:compDelGroup,
  changeGroup:compChangeGroup,
  addDemo:compAddDemo,
  delDemo:compDelDemo,
  refresh:compRefresh,
  refreshDocs:compRefreshDocs,
  refreshMd:compRefreshMd,
  refreshDemos:compRefreshDemos,
  refreshMain:compRefreshMain,
  refreshIndex:compsRefreshIndex
}
export const pluginUtils = {
  add:pluginAdd,
  del:pluginDel,
  addGroup:pluginAddGroup,
  delGroup:pluginDelGroup,
  changeGroup:pluginChangeGroup,
  addDemo:pluginAddDemo,
  delDemo:pluginDelDemo,
  refresh:pluginRefresh,
  refreshDocs:pluginRefreshDocs,
  refreshMd:pluginRefreshMd,
  refreshDemos:pluginRefreshDemos,
  refreshMain:pluginRefreshMain,
  refreshIndex:pluginsRefreshIndex
}